• 问题

    在31条说过在使用enum时最好不要企图通过ordinal()方法以获取枚举元素在枚举类中的索引顺序,然后在其他代码中依赖这种顺序,这样的代码是不安全的。比如代码中数组的索引依赖于enum的序数:

    public class Herb {
        public enum Type {
            ANNUAL, BIENNIAL, PERENNTAL
        }
    
        // private static final Herb Type = null;
        private final String name;
        private final Type type;
    
        Herb(String name, Type type) {
            this.name = name;
            this.type = type;
        }
    
        @Override
        public String toString() {
            return name;
        }
    
        // 数组索引顺序与枚举元素的序数强依赖
        Herb[] garden = { new Herb("1", Type.ANNUAL), new Herb("2", Type.BIENNIAL), new Herb("3", Type.PERENNTAL) };
    
        Set<Herb>[] herbsByType = (Set<Herb>[])new Set[Herb.Type.values().length];
        for(int i = 0;i<herbsByType.length;i++){
            herbsByType[i] = new HashSet<Herb>();
        }
        for(Herb h:garden){
            herbsByType[h.type.ordinal()].add(h);
        }   
    }}
    

    上例中的代码旨在将Herb类型按照enum Type顺序进行排序,在构建的garden数组是与枚举元素的序数强依赖的,加入Type中的枚举元素一不小心调换了顺序,而garden并没有改变,再按照枚举元素在枚举类中的序数从garden数组中获取数据自然而然会出错。

    那么如何解决?

  • 解决

    实际上,上例是想通过枚举值作为数组的索引,是枚举到数组值之间的映射关系,枚举充当着“索引键”的关系,在这样的使用场景下,可以采用EnumMap,该类专门用于枚举键,更加安全可靠。

    • EnumMap的介绍

      1. EnumMap是一个与枚举类一起使用的Map实现,EnumMap中所有key都必须是单个枚举类的枚举值。创建EnumMap时必须显式或隐式指定它对应的枚举类。

      2. EnumMap在内部以数组形式保存,所以这种实现形式非常紧凑、高效。

      3. EnumMap根据key的自然顺序(即枚举值在枚举类中的定义顺序)来维护来维护key-value对的次序。当程序通过keySet()、entrySet()、values()等方法来遍历EnumMap时即可看到这种顺序。

      4. EnumMap不允许使用null作为key值,但允许使用null作为value。如果试图使用null作为key将抛出NullPointerException异常。如果仅仅只是查询是否包含值为null的key、或者仅仅只是使用删除值为null的key,都不会抛出异常。

  • 将上例由EnumMap改写

    Herb[] garden = { new Herb("1", Type.ANNUAL), new Herb("2", Type.BIENNIAL), new Herb("3", Type.PERENNTAL) };
        Map<Herb.Type, Set<Herb>> herbsByType = new EnumMap<Herb.Type,Set<Herb>>(Herb.Type.class);
        for(Herb.Type t:Herb.Type.values()){
            herbsByType.put(t, new HashSet<Herb>());
        }
        for(Herb h:garden)
            herbsByType.get(h.type).add(h); 
        System.out.println(herbsByType);
    

    可以看出由EnumMap改造后,程序更加简洁,也更加安全。总之,试图通过ordinal()方法,利用枚举元素在枚举中的位置做逻辑处理的,这种代码一般都是不可取和不安全的。

  • 总结

    最好不要用序数来索引数组 。如果你所表示的这种关系是多维的 , 就使用 EnumMap<…,EnumMap<…>> 。代码中一般情况下都不使用 Enum.ordinal , 即使要用也是因为特殊情况。

results matching ""

    No results matching ""